﻿#include <stdio.h>
#ifdef WIN32

#else
#include <unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/tcp.h>
#endif

#include <string.h>

#include "SLT_tool_pub.h"
#include "SL_tcpServer_event_impl.h"
#include "SL_tcpClient.h"
#include "SL_tcpClient_event_impl.h"

#define _TCPCLIENT_LOG_DEBUG(_id,_format,...) SLT_LOG(1,SLT_DEBUG,"[tcp]cli(%u) " _format,_id, ##__VA_ARGS__)
#define _TCPCLIENT_LOG_INFO(_id,_format,...) SLT_LOG(1,SLT_INFO,"[tcp]cli(%u) " _format,_id, ##__VA_ARGS__)
#define _TCPCLIENT_LOG_ERR(_id,_format,...) SLT_LOG(1,SLT_ERROR,"[tcp]cli(%u) " _format,_id, ##__VA_ARGS__)

#define EVUTIL_ERR_IS_EAGAIN(e) \
	((e) == WSAEWOULDBLOCK || (e) == EAGAIN)

SLTcpClientEventImpl::SLTcpClientEventImpl(uint32_t _id)
{
	m_clientID = _id;
	m_recvCount = 0;
}
SLTcpClientEventImpl::~SLTcpClientEventImpl()
{
	_TCPCLIENT_LOG_INFO(m_clientID,"client: desotry,%s", toString().c_str());
}
std::string SLTcpClientEventImpl::toString()
{
	char buf[256];
	snprintf(buf, sizeof(buf), "id(%d),fd(%d)l(%X,%d)r(%X,%d)", m_clientID,m_socket,m_lip, m_lport, m_rip, m_rport);
	return std::string(buf);
}
const slIpAddr &SLTcpClientEventImpl::getLocalAddr()
{
	m_laddr.m_ip = m_lip;
	m_laddr.m_port = m_lport;
	return m_laddr;
}
const slIpAddr &SLTcpClientEventImpl::getRemoteAddr()
{
	m_raddr.m_ip = m_rip;
	m_raddr.m_port = m_rport;
	return m_raddr;
}

void SLTcpClientEventImpl::connect2(std::string _serIp, int _serPort, int _timeout, std::string _lip, int _lport)
{
	if (m_workState != tcp_work_state_init) {
		_TCPCLIENT_LOG_ERR(m_clientID,"repeat connet. error");
		return;
	}
	m_rip = SLT_ip2num(_serIp.c_str());
	m_rport = _serPort;

	m_workState = tcp_work_state_conning;
	NetConnect(_serPort, _serIp, _timeout);
}
void SLTcpClientEventImpl::bindCB(SLTcpClientCBPtr _pcb)
{
	m_pcb = _pcb; 
}
void SLTcpClientEventImpl::onSend()
{
	
}
int SLTcpClientEventImpl::send2(const char* _pdata, int _dataLen)
{
	if (m_workState != tcp_work_state_ok) {
		return -10;
	}
	m_isActive = true;
	return  NetWriteSocket(m_socket, _pdata, _dataLen);
}
void SLTcpClientEventImpl::setRecv(bool _enable)
{
	if (m_brecv == _enable) {
		return;
	}
	bool bpre = m_brecv;
	
	_TCPCLIENT_LOG_INFO(m_clientID,"setRecv:%d", _enable);
	m_brecv = _enable;
	if (bpre && !_enable) {
		m_recvCount = 0;
	}

	//考虑接收缓冲区，超过缓冲区停止接收
	_tcp_event_infoStpr  pevinfo(new _tcp_event_info);
	pevinfo->m_cmd = EPOLL_LIST_SOCKET_STARTRECV;
	pevinfo->m_client = shared_from_this();
	m_pserver->pushEvent(pevinfo);  //切换线程调用createFd
}
void SLTcpClientEventImpl::recv2()
{
	//printf("[SLTcpClientEventImpl] recv2,fd:%d,count:%d,recvstate:%d\n", m_socket,(int)m_recvCount, m_brecv);
	if (m_brecv) {
		return;
	}

	if (m_recvCount < 0) {
		m_recvCount = 0;
	}
	if (m_recvCount == 0) {
		m_recvCount++;
		_tcp_event_infoStpr  pevinfo(new _tcp_event_info);
		pevinfo->m_cmd = EPOLL_LIST_SOCKET_RECVDATAONE;
		pevinfo->m_client = shared_from_this();
		m_pserver->pushEvent(pevinfo);  //切换线程调用createFd
	}
	else {
		m_recvCount++;
	}
}
void SLTcpClientEventImpl::close2()
{
	//统一入口关闭socket，避免socket重复分配，被关闭
	_TCPCLIENT_LOG_INFO(m_clientID,"user close fd:%d", m_socket);
	_close2();
}
void SLTcpClientEventImpl::_close2()
{
	m_socketLock.lock();
	if (-1 == m_socket) {
		m_socketLock.unlock();
		return;
	}
	
	//关闭连接
	_tcp_event_infoStpr  pevinfo(new _tcp_event_info);
	pevinfo->m_cmd = EPOLL_LIST_SOCKET_DEL;
	pevinfo->m_client = shared_from_this();
	pevinfo->m_socket = m_socket;
	m_pserver->pushEvent(pevinfo);

	m_socket = -1;
	m_sendBuf.reset();
	m_socketLock.unlock();
}
int SLTcpClientEventImpl::NetConnect(int port, const std::string& host, int timeOut)
{
	struct sockaddr_in sin4;
	//bzero(&sin4, sizeof(sin4));
	memset(&sin4, 0, sizeof(sin4));
	sin4.sin_family = AF_INET;
	sin4.sin_port = htons(port);

	/* 将ip转网络二进制 */
	if (1 != inet_pton(AF_INET, host.c_str(), &(sin4.sin_addr)))
	{
		_TCPCLIENT_LOG_ERR(m_clientID, "Error : unknow host(%s)", host.c_str());
		return -1;
	}

	int nSocket;
	if (-1 == (nSocket = socket(AF_INET, SOCK_STREAM, 0))){
		_TCPCLIENT_LOG_ERR(m_clientID, "Error on creat socket : %u", errno);
		return -1;
	}

	if (0 != NetSetNoDelay(nSocket)){
		_TCPCLIENT_LOG_ERR(m_clientID,"Error on NetSetNoDelay : %u", errno);
		NetClose(nSocket);
		return -1;
	}

	if (0 != NetSetSendTimeo(nSocket, timeOut)) {
		_TCPCLIENT_LOG_ERR(m_clientID, "Error on NetSetSendTimeo : %u", errno);
		NetClose(nSocket);
		return -1;
	}

#if 1
	//同步连接
	_TCPCLIENT_LOG_INFO(m_clientID, "connect start,fd:%d", nSocket);
	int cret = connect(nSocket, (struct sockaddr*)&sin4, sizeof(sin4));
	if (cret < 0) {
		_TCPCLIENT_LOG_ERR(m_clientID, "tcp connect error: %d", errno);
		NetClose(nSocket);
		m_workState = tcp_work_state_init;
		m_pcb->TcpClient_onConnect(tcp_error_type_err);
		return -1;
	}
	//SLTcpClientEventImpl::NoBlock(nSocket);
	evutil_make_socket_nonblocking(nSocket);
	m_socket = nSocket;   //保存句柄

	_tcp_event_infoStpr  pevinfo(new _tcp_event_info);
	pevinfo->m_cmd = EPOLL_LIST_CLIENT_CONNECT;
	pevinfo->m_socket = nSocket;
	pevinfo->m_client = shared_from_this();
	pevinfo->m_connectState = 0;
	m_pserver->pushEvent(pevinfo);

	_TCPCLIENT_LOG_INFO(m_clientID,"connect success,fd:%d", m_socket);
	m_workState = tcp_work_state_ok;
	m_pcb->TcpClient_onConnect(tcp_error_type_ok);
#endif
#if 0
	//异步没有完成。后期完成
	SLTcpClientEpollImpl::NoBlock(nSocket);
	int cret = connect(nSocket, (struct sockaddr*)&sin4, sizeof(sin4));
	if (0 == cret) {
		//m_workState = tcp_work_state_ok;
		//m_pcb->TcpClient_onConnect(tcp_error_type_ok);
		_tcp_event_infoStpr  pevinfo(new _tcp_event_info);
		pevinfo->m_cmd = EPOLL_LIST_CLIENT_CONNECT;
		pevinfo->m_client = shared_from_this();
		pevinfo->m_connectState = 0;
		m_pserver->pushEvent(pevinfo);  //切换线程调用createFd
	}
	else {
		// -1
		if (errno != EINPROGRESS) {
			printf("tcp connect error: %d\n", errno);
			NetWork::NetClose(nSocket);
			m_workState = tcp_work_state_init;
			m_pcb->TcpClient_onConnect(tcp_error_type_err);
			return -1;
		}

		printf("tcp connect EINPROGRESS\n");
		_tcp_event_info einfo;
		einfo.m_cmd = EPOLL_LIST_CMD_CONNECT;
		einfo.m_socket = nSocket;
		einfo.m_clientPtr = shared_from_this();
		m_pserver->postEvent(einfo);
		//m_pserver->SendEventSig(nSocket, EPOLL_SOCKET_CONNECT);
	}
#endif
	return nSocket;
}
void SLTcpClientEventImpl::onRecvData(char* _pdata, int _len)
{
	if (m_workState != tcp_work_state_ok) {
		return;
	}

	m_isActive = true;
	m_pcb->TcpClient_onRecv(_pdata,_len); //故意不保护，如果流程有问题，这里回奔溃
}
void SLTcpClientEventImpl::onError(tcp_error_type _type)
{
	clearEvent();
	//只有server单线程回调
	if (m_workState == tcp_work_state_conning) {
		_TCPCLIENT_LOG_INFO(m_clientID, "connect error");
		m_workState = tcp_work_state_init;
		m_pcb->TcpClient_onConnect(tcp_error_type_err);
		return;
	}

	if (m_workState != tcp_work_state_ok) {
		return;
	}
	m_workState = tcp_work_state_init;  //必须放在close前面
	_TCPCLIENT_LOG_INFO(m_clientID, "onError fd:%d,type:%d", m_socket, _type);
	_close2();
	m_pcb->TcpClient_onError(_type);
}
void SLTcpClientEventImpl::NoBlock(int fd)
{
	int mode = 1;
#ifdef WIN32
	if (0 != ioctlsocket(fd, FIONBIO, reinterpret_cast<u_long FAR*>(&mode)))
	{
		//_TCPCLIENT_LOG_ERR(m_clientID, "ioctlsocket error! closesocket fd:%d", fd);
		closesocket(fd);
	}
#else
	int flags = fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#endif
}

int SLTcpClientEventImpl::NetWriteSocket(int nSocket, const char* writeBuf, int writeSize,bool _sendcache)
{
	//目前没有做异步发送，暂时发送1s
	long lWrite, lLeft, lOffset;
	if (!_sendcache &&  writeSize == 0) {
		_TCPCLIENT_LOG_ERR(m_clientID, "NetWriteSocket warning，writeSize 0,fd:%d", nSocket);
		return 0;
	}

	std::unique_lock<std::mutex>  ul(m_socketLock);
	if (nSocket == -1) {
		_TCPCLIENT_LOG_DEBUG(m_clientID, "NetWriteSocket nSocket fd:%d", nSocket);
		return -1;
	}

	if(_sendcache){
		if(0 == m_sendBuf.m_len){
			//_TCPCLIENT_LOG_DEBUG(m_clientID, "send2net sendbuf len 0, fd:%d", m_socket);
			return 0;
		}
		lLeft = (long)m_sendBuf.m_len;
		writeBuf = (const char*)m_sendBuf.get();
	}else{
		if (m_sendBuf.m_len > 0) {
			return tcp_send_error_again;  //避免重复进入造成缓存丢失
		}
		lLeft = writeSize;
	}
	lOffset = 0;
	
	//SLT_elapseTimer  start;
	while (lLeft > 0){
		
		//_TCPCLIENT_LOG_ERR(m_clientID, "NetWriteSocket send fd:%d,lOffset:%d,leftlen:%d\n", nSocket, lOffset,lLeft);
		lWrite = send(nSocket, writeBuf + lOffset, lLeft,0);
		if (lWrite < 0)	{
			if (errno == EINTR) {
				continue;
			}
			if (errno == EAGAIN) {
			//int e = evutil_socket_geterror(nSocket);
			//if(EVUTIL_ERR_IS_EAGAIN(e)){
				//保存剩余的数据
				SLT_memBuf tempbuf = m_sendBuf;
				m_sendBuf.add(writeBuf + lOffset, lLeft);
				_TCPCLIENT_LOG_DEBUG(m_clientID, "NetWriteSocket send warning. again fd:%d,lOffset:%d,leftlen:%d\n", nSocket,lOffset, lLeft);
				return lOffset + lLeft;
			}

			_TCPCLIENT_LOG_ERR(m_clientID, "NetWriteSocket send error.lWrite:%d,err:%d",lWrite,errno);
			m_sendBuf.reset();
			return -3;
		}
		else if (lWrite == 0)
		{
			_TCPCLIENT_LOG_ERR(m_clientID, "NetWriteSocket send error.0,fd:%d\n", nSocket);
			return -2;
		}

		lOffset += lWrite;
		lLeft -= lWrite;
		//_TCPCLIENT_LOG_ERR(m_clientID,"send ok:%u,off:%u,left:%u", lWrite, lOffset, lLeft);
	}

	/*if (start.elapseMs() > 100) {
		_TCPCLIENT_LOG_ERR(m_clientID, "send finish warning.time long,fd:%d,len:%d\n", nSocket, writeSize);
	}*/

	if(_sendcache){
		_TCPCLIENT_LOG_DEBUG(m_clientID, "NetWriteSocket onsend notify");
		m_sendBuf.reset();
		m_pcb->TcpClient_onSend();
	}
	return lOffset;
}
int SLTcpClientEventImpl::NetSetNoDelay(int nSocket)
{
	int enable = 1;
	if (setsockopt(nSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&enable, sizeof(enable)) != 0)	{
		//strcpy(errStr, "error on setsockopt (TCP_NODELAY)");
		return -1;
	}
	return 0;
}
int SLTcpClientEventImpl::NetSetSendTimeo(int nSocket,int _outMs)
{
	int os = _outMs / 1000;
	int ous = _outMs % 1000 * 1000;
	struct timeval tv;
	tv.tv_sec = os;
	tv.tv_usec = ous;
	if (setsockopt(nSocket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)) != 0)	{
		return -1;
	}
	return 0;
}
void SLTcpClientEventImpl::NetClose(int socket)
{
	if (socket == -1) {
		return;
	}
#ifdef WIN32
    closesocket(socket);
#else
    close(socket);
#endif
}

void  SLTcpClientEventImpl::onSend2Net()
{
	if (m_workState != tcp_work_state_ok) {
		//_TCPCLIENT_LOG_ERR(m_clientID, "send2net state error:fd:%d", m_socket);
		return;
	}
	NetWriteSocket(m_socket, nullptr,0,true);
}

void SLTcpClientEventImpl::onRecvFromNet()
{
	//读取数据
	int ret = 0;
	while (1) {
		if (m_brecv) {
			ret = onRecvFromNetInvoke(false);
			if (ret <= 0) {
				return;
			}
		}
		else {
			//_TCPSERVER_LOG_DEBUG("[tcpser] no recv,fd:%d,count:%d\n", _pclient->m_socket,(int)_pclient->m_recvCount);
			if (m_recvCount > 0) {
				ret = onRecvFromNetInvoke(true);
				if (ret <= 0) {
					return;
				}
			}
			else {
				break;
			}
		}
	}
	return;   //返回值与onRecvFromNetInvoke 内部返回值统一处理
}
int SLTcpClientEventImpl::onRecvFromNetInvoke(bool _deccount)
{
	if(m_socket < 0){
		return 0;  //只有用户执行了close，才会为-1。结束，等待用户close处理
	}
	char* precvbuf = m_recvBuf;
	int tempRead = NetReadSocketOne( m_socket, precvbuf, m_recvLen);  //接收长度必须比buf小
	if (tempRead < 0) {
#ifdef WIN32
		if (WSAGetLastError() == WSAEWOULDBLOCK) {
			return 0;
		}
#else
		if (errno == EWOULDBLOCK || errno == EAGAIN) {
			//printf("[tcpser]socket read error:%d\n", errno);
			return 0;
		}
#endif
		_TCPCLIENT_LOG_ERR(m_clientID,"[tcpser]socket read error:%d", errno);
		//异常socket
		onError(tcp_error_type_err);
		return -1;
	}
	else if (tempRead == 0) {
		_TCPCLIENT_LOG_INFO(m_clientID,"socket read 0.close, fd:%d", m_socket);
		onError(tcp_error_type_remoteclose);
		return -2;
	}
	//_TCPCLIENT_LOG_ERR(m_clientID, "[tcpser]socket read,fd:%d, len:%d\n", m_socket, tempRead);
	//printf("[tcpser]socket read,fd:%d, len:%d\n", _pclient->m_socket, tempRead);
	m_recvBuf[tempRead] = 0;   //保证打印的数据不会奔溃，接收长度必须比buf小
	onRecvData(m_recvBuf, tempRead);
	if (_deccount) {
		m_recvCount--;
	}
	
	return tempRead;
}
int SLTcpClientEventImpl::NetReadSocketOne(int nSocket, char* readBuf, int readSize)
{
	long lRead;
again_read:
	//lRead = read(nSocket, readBuf, readSize);
	lRead = recv(nSocket, readBuf, readSize,0);
	if (lRead < 0) {
#ifdef WIN32
		//_TCPCLIENT_LOG_DEBUG(m_clientID, "NetReadSocketOne recv error.lRead:%d,WSAGetLastError:%d", lRead, WSAGetLastError());
		if (WSAGetLastError() == WSAEINTR)
			goto again_read;
#else
		if (errno == EINTR)
			goto again_read;
#endif
		//_TCPCLIENT_LOG_DEBUG(m_clientID, "NetReadSocketOne recv error.lRead:%d,err:%d", lRead, errno);
		return -2;
	}
	return lRead;
}


int SLTcpClientEventImpl::initSocket(struct event_base* base, int nsocket)
{
    m_general_evwent = event_new( base,nsocket, EV_WRITE | EV_READ | EV_CLOSED | EV_ET  |EV_PERSIST,
        [](evutil_socket_t fd, short events, void* arg){
            reinterpret_cast<SLTcpClientEventImpl*>(arg)->EventProssess(fd, events);
        },
        reinterpret_cast<void*>(this));
    
    if (m_general_evwent == NULL) {
		_TCPCLIENT_LOG_ERR(m_clientID,"initSocket memory error");
        return -1;
    }

	// m_write_event = event_new(base,nsocket,EV_ET  |EV_PERSIST |EV_WRITE,
	// 	[](evutil_socket_t fd, short events, void* arg){
	// 		reinterpret_cast<SLTcpClientEventImpl*>(arg)->EventProssess(fd, events);
	// 	},
	// 	reinterpret_cast<void*>(this));
	// event_add(m_write_event, nullptr);

    if(0 != event_add(m_general_evwent, nullptr)){
		 _TCPCLIENT_LOG_ERR(m_clientID,"event_add  error !!!!!!!!!!!!!");
		 return -1;
	}
	
    return 0;
}
void SLTcpClientEventImpl::EventProssess(int nSocket, short events)
{
	//socket 接收，发送，事件触发
	//不读也会收到close事件
	//读信号会单独回调吗？如果不读，写也不处理了ma？
    do {
		if (events & EV_WRITE){
			//_TCPCLIENT_LOG_DEBUG(m_clientID,"event write, %02X", events);
			onSend2Net();
			if(! (events & EV_READ)){
				return;
			}
        }

        if (events & EV_READ) {
			//_TCPCLIENT_LOG_DEBUG(m_clientID,"event read, %02X", events);
			onRecvFromNet();
			return;
        }
        
        if (events & EV_CLOSED){
			_TCPCLIENT_LOG_DEBUG(m_clientID,"event close, %02X", events);
			onError(tcp_error_type_remoteclose);
			return;
		} 

    } while (0);
    _TCPCLIENT_LOG_ERR(m_clientID,"event error , %02X", events);
	_close2();
    event_del(m_general_evwent);
	event_free(m_general_evwent);
	m_general_evwent = nullptr;
}

void SLTcpClientEventImpl::clearEvent()
{
	if(m_write_event){
		event_del(m_write_event);
        event_free(m_write_event);
        m_write_event = nullptr;
	}
    if (m_general_evwent) {
        event_del(m_general_evwent);
        event_free(m_general_evwent);
        m_general_evwent = nullptr;
    }

   
}